/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Utils;

import ImagenesDBGad.IO.Imagen;
import ImagenesDBGad.IO.Objeto;
import ImagenesDBGad.IO.Punto;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGOMPathElement;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.NodeList;
import org.w3c.dom.svg.SVGDocument;

/**
 *
 * @author Ivan Pianetti @BIPiane
 * http://code.google.com/p/imagenesdb-gad/source/browse/
 */
public class SVGUtils {

    private static Imagen imgSVGPrincipal = new Imagen();
    final static int giroRotacion = 5; //5 grados
    private static float porcentaje = 2; // Porcentaje del perimetro para obtener puntos

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String url = ArchUtils.seleccionarArchivo();
        imgSVGPrincipal = cargarImagen(url);

        System.out.println("\nNombre: " + imgSVGPrincipal.getNombre());
        System.out.println("XML: " + imgSVGPrincipal.getCodXML());
        System.out.println("Objetos: " + imgSVGPrincipal.getObjetos().size());
    }

    public static Imagen cargarImagen(String url) {
        imgSVGPrincipal = new Imagen();
        try {
            String xmlImagen = ArchUtils.getXML(url);
            imgSVGPrincipal.setCodXML(xmlImagen);
            imgSVGPrincipal.setNombre(ArchUtils.getNombreArchivo(url));
            System.out.println("\nNombre: " + imgSVGPrincipal.getNombre());

            String parserPrincipal = XMLResourceDescriptor.getXMLParserClassName();
            SAXSVGDocumentFactory factoryPrincipal = new SAXSVGDocumentFactory(parserPrincipal);
            SVGDocument docPrincipal = (SVGDocument) factoryPrincipal.createDocument(url);

            NodeList nodoListPrincipal = docPrincipal.getRootElement().getElementsByTagName("path");
            for (int i = 0; i < nodoListPrincipal.getLength(); i++) {
                SVGOMPathElement pathElemPrincipal = (SVGOMPathElement) nodoListPrincipal.item(i);
                String caminoObj = pathElemPrincipal.getAttribute("d");

                ArchUtils.crearSVGAuxiliar(caminoObj);

                String parserAux = XMLResourceDescriptor.getXMLParserClassName();
                SAXSVGDocumentFactory factoryAux = new SAXSVGDocumentFactory(parserAux);

                String urlAUX = ArchUtils.urlSVGAuxiliar;
                SVGDocument docAux = (SVGDocument) factoryAux.createDocument("file:" + urlAUX);

                GVTBuilder builder = new GVTBuilder();
                BridgeContext bringeCtx = new BridgeContext(new UserAgentAdapter());
                GraphicsNode graphicNodeRoot = builder.build(bringeCtx, docAux);
                Rectangle2D bBox = graphicNodeRoot.getSensitiveBounds();

                int alto = (int) Math.rint(bBox.getHeight());
                int ancho = (int) Math.rint(bBox.getWidth());
                int x = (int) Math.rint(bBox.getX());
                int y = (int) Math.rint(bBox.getY());

                //Debe haber solo un path element ya que hay un solo SVG (documento) por Objeto
                SVGOMPathElement pathElementAux = (SVGOMPathElement) docAux.getRootElement().getElementsByTagName("path").item(0);

                //int puntos = Math.round(5 * pathElementAux.getTotalLength() / 100);
                float acumulado = 0;
                float incremento = (porcentaje * pathElementAux.getTotalLength() / 100);

                Objeto objeto = new Objeto();
                objeto.setXmlPath(caminoObj);

                objeto.setV1x(x);
                objeto.setV1y(y);
                objeto.setV2x(ancho + x);
                objeto.setV2y(alto + y);

                ArrayList<Punto> puntosCalculados = new ArrayList<Punto>();
                while (acumulado < pathElementAux.getTotalLength()) {
                    //for (int p = 0; p < puntos; p++) {
                    Punto pto = new Punto();
                    pto.setX(pathElementAux.getPointAtLength(acumulado).getX());
                    pto.setY(pathElementAux.getPointAtLength(acumulado).getY());
                    acumulado = acumulado + incremento;
                    pto.setObjeto(objeto);
                    puntosCalculados.add(pto);
                    //}
                }
                objeto.setPuntos(puntosCalculados);

                objeto.setImagen(imgSVGPrincipal);
                imgSVGPrincipal.getObjetos().add(rotarObjeto(objeto));
            }
        } catch (IOException ex) {
        }
        return imgSVGPrincipal;
    }


    /*
     * Este método se encarga de poner en vertical el objeto
     */
    private static Objeto rotarObjeto(Objeto objeto) {
        //Relación entre alto y ancho del Obj Viejo
        float relacionViejo = (objeto.getV2y() - objeto.getV1y()) / (objeto.getV2x() - objeto.getV1x());
        float xMinViejo = objeto.getV1x();
        float yMinViejo = objeto.getV1y();
        float xMaxViejo = objeto.getV2x();
        float yMaxViejo = objeto.getV2y();
        float xCentroViejo = ((objeto.getV2x() - objeto.getV1x()) / 2) + objeto.getV1x();
        float yCentroViejo = ((objeto.getV2y() - objeto.getV1y()) / 2) + objeto.getV1y();

        ArrayList<Punto> puntosNuevo = new ArrayList<Punto>();
        float relacionNueva = 0;
        float xMinNuevo = Float.MAX_VALUE;
        float yMinNuevo = Float.MAX_VALUE;
        float xMaxNuevo = Float.MIN_VALUE;
        float yMaxNuevo = Float.MIN_VALUE;
        float xCentroNuevo;
        float yCentroNuevo;

        int grados = giroRotacion;
        while ((grados <= 360 || relacionViejo > relacionNueva) && grados <= 360) {
            puntosNuevo.clear();
            relacionNueva = 0;
            xMinNuevo = Float.MAX_VALUE;
            yMinNuevo = Float.MAX_VALUE;
            xMaxNuevo = Float.MIN_VALUE;
            yMaxNuevo = Float.MIN_VALUE;
            xCentroNuevo = 0;
            yCentroNuevo = 0;

            for (Iterator<Punto> it = objeto.getPuntos().iterator(); it.hasNext();) {
                Punto pto = it.next();
                Punto ptoNuevo = new Punto();

                ptoNuevo.setX(xCentroViejo + (pto.getX() - xCentroViejo) * Math.cos(grados) - (pto.getY() - yCentroViejo) * Math.sin(grados));
                ptoNuevo.setY(yCentroViejo + (pto.getX() - xCentroViejo) * Math.sin(grados) + (pto.getY() - yCentroViejo) * Math.cos(grados));
                puntosNuevo.add(ptoNuevo);

                if (ptoNuevo.getX() < xMinNuevo) {
                    xMinNuevo = (float) ptoNuevo.getX();
                }
                if (ptoNuevo.getX() > xMaxNuevo) {
                    xMaxNuevo = (float) ptoNuevo.getX();
                }
                if (ptoNuevo.getY() < yMinNuevo) {
                    yMinNuevo = (float) ptoNuevo.getY();
                }
                if (ptoNuevo.getY() > yMaxNuevo) {
                    yMaxNuevo = (float) ptoNuevo.getY();
                }
            }
            xCentroNuevo = xMinNuevo + (xMaxNuevo - xMinNuevo) / 2;
            yCentroNuevo = yMinNuevo + (yMaxNuevo - yMinNuevo) / 2;
            relacionNueva = (yMaxNuevo - yMinNuevo) / (xMaxNuevo - xMinNuevo);

            if (relacionNueva > relacionViejo) {
                relacionViejo = relacionNueva;
                xMinViejo = xMinNuevo;
                xMaxViejo = xMaxNuevo;
                yMinViejo = yMinNuevo;
                yMaxViejo = yMaxNuevo;
                xCentroViejo = xCentroNuevo;
                yCentroViejo = yCentroNuevo;
                objeto.getPuntos().clear();
                objeto.getPuntos().addAll(puntosNuevo);
            }
            grados += giroRotacion;
        }

        objeto.setV1x(xMinViejo);
        objeto.setV1y(yMinViejo);
        objeto.setV2x(xMaxViejo);
        objeto.setV2y(yMaxViejo);

        for (Punto p : objeto.getPuntos()) {
            p.setObjeto(objeto);
        }

        return objeto;
    }
}
